package subjects

import (
	"binseek/pkg/finder"
	"binseek/pkg/utils"
)

const IPADDR_POSITION_SIZE = 3

type IpAddr struct { // 新设计的IP格式
	*finder.Pandect
}

func NewIpAddr(fileName string) (*IpAddr, error) {
	pand, err := finder.NewPandect(fileName, 3, true)
	if err != nil {
		return nil, err
	}
	pr, posLst := &IpAddr{pand}, make([]int64, 3)
	if posLst, err = pand.ReadPositions(true); err != nil {
		return pr, err
	}
	lastPos, firstPos, appendPos := posLst[0], posLst[1], posLst[2]
	fixSize := int(pr.FileSize - lastPos)
	indexSize := int(lastPos-firstPos) + fixSize
	if err = pr.CreateIndex(fixSize, indexSize, firstPos); err != nil {
		return pr, err
	}
	err = pr.CreateAppendix(appendPos, firstPos)
	return pr, err
}

func (p *IpAddr) GetRecord(posData, relPosData, infoData []byte) ([]byte, error) { // 新的IP格式记录
	utils.DebugPrint("GetRecord:")
	flag := finder.ReadFlag(utils.FirstByte(posData))
	if flag == finder.ReadAppendix {
		utils.DebugPrint("ReadAppendix:")
		idx := int(utils.Uint64(posData[1:], p.IsBom))
		data, err := p.Appendix.ReadIndex(idx, 0)
		if err != nil {
			return infoData, err
		}
		return append(data, infoData...), nil
	}

	var data []byte
	err := p.JumpTo(posData, relPosData, true)
	_, data, err = p.AheadRead(IPADDR_POSITION_SIZE, finder.RECORD_MAX_SIZE, flag)
	if err != nil {
		return infoData, err
	}
	flag = finder.ReadFlag(utils.FirstByte(data))
	if flag == finder.ReadAppendix {
		posData, infoData = data[:3], append(data[3:], infoData...)
	} else {
		relPosData, infoData = data[:2], append(data[2:], infoData...)
	}
	return p.GetRecord(posData, relPosData, infoData)
}

func (p *IpAddr) Find(ip string) (info string, err error) {
	var idxData, infoData []byte
	target := utils.Ip2Bytes(ip)
	if idxData, err = p.SearchIndex(target); err != nil {
		return
	}
	posData, tailByte2, tailByte3 := idxData[6:], idxData[4], idxData[5]
	if tailByte2 != 0x00 || tailByte3 != 0x00 { // 以两个0的为结束IP跨度大，忽略比较
		tailData := append(idxData[:2], tailByte2, tailByte3)
		if utils.Compare(target, tailData, p.IsBom) > 0 {
			return
		}
	}
	if infoData, err = p.GetRecord(posData, []byte{0x00}, infoData); err == nil {
		info = string(infoData)
	}
	return
}
